import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import buffer.Buffer;
import buffer.FIFO;

public class ProducerConsumerProblem {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new Thread(ServiceLocator.getProducer(), "P").start();
		new Thread(ServiceLocator.getConsumer(), "C").start();
	}

}

class Consumer implements Runnable {

	private static Log log = LogFactory.getLog(Consumer.class.getName());

	private Buffer<String> buffer;

	public Consumer(Buffer<String> buffer) {
		this.buffer = buffer;
	}

	@Override
	public void run() {
		String line;

		do {

			if (buffer.isEmpty()) {
				
				// deadlock happens if producer will fill the buffer while we
				// are inside if block, but not yet sleeping
				log.debug("buffer is empty, waiting...");
				sleep();
			}
			log.debug("have the line in the buffer, reading it");

			// synchronized (buffer) {
			line = buffer.get();
			notifyProducer();
			// }
			System.out.println(line);
		} while (!line.equals("."));
	}

	public void sleep() {
		synchronized (this) {
			try {
				wait();
			} catch (InterruptedException ex) {

			}
		}
	}

	public void notifyProducer() {
		synchronized (ServiceLocator.getProducer()) {
			ServiceLocator.getProducer().notifyAll();
		}
	}
}

class Producer implements Runnable {
	private static Log log = LogFactory.getLog(Producer.class.getName());

	private Buffer<String> buffer;

	public Producer(Buffer<String> buffer) {
		this.buffer = buffer;
	}

	@Override
	public void run() {
		// for (String line : Text.text) {
		int i = 0;
		while (true) {

			i++;
			if (buffer.isFull()) {
				log.debug("buffer is full, waiting for free space...");
				sleep();
			}

			log.debug("writing the line to the buffer");

			// synchronized (buffer) {
			buffer.put("[ " + i + " ]");
			notifyConsumer();
			// }
		}
	}

	public void sleep() {
		synchronized (this) {
			try {
				wait();
			} catch (InterruptedException ex) {

			}
		}
	}

	public void notifyConsumer() {
		synchronized (ServiceLocator.getConsumer()) {
			ServiceLocator.getConsumer().notifyAll();
		}
	}

}

class Text {

	public static final String[] text = { "Хотіла б я піснею стати",
			"У сюю хвилину ясну,", "Щоб вільно по світі літати,",
			"Щоб вітер розносив луну.", "Щоб геть аж під яснії зорі",
			"Полинути співом дзвінким,", "Упасти на хвилі прозорі,",
			"Буяти над морем хибким.", "Лунали б тоді мої мрії",
			"І щастя моє таємне,", "Ясніші, ніж зорі яснії,",
			"Гучніші, ніж море гучне. ", "." };

}

class ServiceLocator {
	private static Buffer<String> buffer = new FIFO<String>(1);
	private static Producer producer;
	private static Consumer consumer;

	public static Producer getProducer() {
		if (producer == null) {
			producer = new Producer(buffer);
		}
		return producer;
	}

	public static Consumer getConsumer() {
		if (consumer == null) {
			consumer = new Consumer(buffer);
		}
		return consumer;
	}
}